本文同步更新於blog
前情提要:鐵路運輸系統,參考範例:運輸系統(工廠方法模式)
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use ReflectionClass;
class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $model = $modelFactory->createModel();
        return $model->getName();
    }
    /**
     * @param string $model
     * @return ModelFactory
     */
    private function createModelFactory($model)
    {
        $namespace = 'App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories';
        $className = $model . 'Factory';
        $reflector = new ReflectionClass($namespace . '\\' . $className);
        return $reflector->newInstance();
    }
}
需求一:各交通工具準備就緒,要開始處理內裝問題。
由於不同的機型會有不同的內裝,讓我們從定義椅子介面開始。
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts;
interface Chair
{
    public function getName();
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
interface ModelFactory
{
    public function createModel(): Model;
    public function createChair(): Chair;
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
class LongChair implements Chair
{
    public function getName()
    {
        return '長型座椅';
    }
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
class ReservedSeatChair implements Chair
{
    public function getName()
    {
        return '對號座椅';
    }
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
class PlaneChair implements Chair
{
    public function getName()
    {
        return '飛機座椅';
    }
}
接著改寫各個機型工廠,選擇適合的座椅。
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\LocalTrain;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\LongChair;
class LocalTrainFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new LocalTrain();
    }
    public function createChair(): Chair
    {
        return new LongChair();
    }
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\SemiExpress;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\ReservedSeatChair;
class SemiExpressFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new SemiExpress();
    }
    public function createChair(): Chair
    {
        return new ReservedSeatChair();
    }
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\LimitedExpress;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\ReservedSeatChair;
class LimitedExpressFactory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊...
        return new LimitedExpress();
    }
    public function createChair(): Chair
    {
        return new ReservedSeatChair();
    }
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Model;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Model\Boeing747;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\Chair;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Chair\PlaneChair;
class Boeing747Factory implements ModelFactory
{
    public function createModel(): Model
    {
        //取得生產材料...
        //招募技術團隊..
        return new Boeing747();
    }
    public function createChair(): Chair
    {
        return new PlaneChair();
    }
}
<?php
namespace App\FactoryPattern\Transport\AbstractFactoryPattern;
use App\FactoryPattern\Transport\AbstractFactoryPattern\Contracts\ModelFactory;
use ReflectionClass;
class Program
{
    /**
     * @param string $model
     * @return string
     */
    public function getModel($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $model = $modelFactory->createModel();
        return $model->getName();
    }
    /**
     * @param string $model
     * @return string
     */
    public function getChair($model)
    {
        $modelFactory = $this->createModelFactory($model);
        $chair = $modelFactory->createChair();
        return $chair->getName();
    }
    /**
     * @param string $model
     * @return ModelFactory
     */
    private function createModelFactory($model)
    {
        $namespace = 'App\FactoryPattern\Transport\AbstractFactoryPattern\ModelFactories';
        $className = $model . 'Factory';
        $reflector = new ReflectionClass($namespace . '\\' . $className);
        return $reflector->newInstance();
    }
}
透過工廠介面,我們組合了各種類型產品。
使得不同類型的產品之間,有了產品族的聯繫關係。
[單一職責原則]
我們將工廠類別 (Creator) 與產品類別 (Product) 視作兩種不同的職責。
[開放封閉原則]
當新增新的產品族時,我們僅需新增工廠類別。
當修改既有產品時,我們僅需修改其產品類別,不會影響到其他的產品類別。
[介面隔離原則]
工廠介面:用來創建機型及座椅。
產品介面:用來創建對應的產品。
[依賴反轉原則]
工廠介面依賴於抽象的機型介面與座椅介面。
工廠實作抽象的工廠介面。
機型實作抽象的機型介面。
座椅實作抽象的座椅介面。
最後附上類別圖:
(註:若不熟悉 UML 類別圖,可參考UML類別圖說明。)
ʕ •ᴥ•ʔ:雖然有點複雜,但這個模式好美。